﻿using InvestmentIntelligence.DB.Utils.MergeExtension;
using InvestmentIntelligence.DbModel.Models;
using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace InvestmentIntelligence.Data.Repository.EFImplementation
{
    public class OriginalPositionRepository : IIPRepository<OriginalPosition>, IOriginalPositionRepository
    {

        public List<T> GetListByBook<T>(Func<IQueryable<OriginalPosition>, IEnumerable<T>> f, int bookId)
        {
            return GetList(dc => f(dc.Where(c => c.FundBookEntityId == bookId)));
        }

        public new List<T> GetList<T>(Func<IQueryable<OriginalPosition>, IEnumerable<T>> f)
        {
            return base.GetList(f);
        }

        public DateTime? GetMinDate()
        {
            return Get(c => c.Min(v => (DateTime?)v.Date));
        }

        public DateTime? GetMaxDate()
        {
            return Get(c => c.Max(v => (DateTime?)v.Date));
        }

        public DateTime? GetLastDate(int key)
        {
            return Get(v => v.Where(c => c.FundBookEntityId == key).Max(c => (DateTime?)c.Date));
        }

        public void MergeOverFundBookFromDate(IEnumerable<OriginalPosition> positions, int fundBookEntityId, DateTime fromDate, MergeOperationType mergeType)
        {
            const string appendCmd = @"delete OriginalPosition
from TradingData.OriginalPosition
left join {Source}  t on OriginalPosition.FundBookEntityId = t.FundBookEntityId and OriginalPosition.SecurityId = t.SecurityId and OriginalPosition.Date = t.Date
where OriginalPosition.FundBookEntityId = @FundBookEntityId and OriginalPosition.Date >= @Date and t.FundBookEntityId is null";
            RunUnitOfWork(dc =>
            {
                dc.Merge(positions,
                    MergeOperationType.InsertUpdate,
                    c => mergeType == MergeOperationType.InsertUpdateDelete ? c + appendCmd : c,
                    c =>
                    {
                        c.Parameters.AddWithValue("@Date", fromDate);
                        c.Parameters.AddWithValue("@FundBookEntityId", fundBookEntityId);
                        return c;
                    },
                    0);
                return default(OriginalPosition);
            });
        }

        public Dictionary<int, OriginalPosition> GetActivePositions(int fundBookId)
        {
            var maxDate = GetLastDate(fundBookId).GetValueOrDefault();
            return InternalGet(
                dc =>
                {
                    var context = ((IObjectContextAdapter)dc).ObjectContext;
                    context.CommandTimeout = 0;
                    return (from p in dc.OriginalPositions
                            where p.FundBookEntityId == fundBookId && p.Date == maxDate && p.CloseQuantity != 0
                            select p);
                }, v => v.ToDictionary(p => p.SecurityId));
        }

        public Dictionary<int, List<OriginalPosition>> GetLastPositions(int fundBookId, IEnumerable<int> securityIds)
        {
            var positions = InternalGet(
                dc =>
                {
                    var context = ((IObjectContextAdapter)dc).ObjectContext;
                    context.CommandTimeout = 0;
                    return (from p in dc.OriginalPositions
                           where p.FundBookEntityId == fundBookId && securityIds.Contains(p.SecurityId)
                           select p);
                }, v => v.ToList());

            var groupedPositions = new Dictionary<int, List<OriginalPosition>>();
            foreach (var pos in positions)
            {
                if (groupedPositions.ContainsKey(pos.SecurityId)) groupedPositions[pos.SecurityId].Add(pos);
                else groupedPositions.Add(pos.SecurityId, new List<OriginalPosition> { pos });
            }

            var result = new Dictionary<int, List<OriginalPosition>>();
            foreach (var gPos in groupedPositions)
            {
                var orderedPositions = gPos.Value.OrderByDescending(s => s.Date);
                var list = new List<OriginalPosition>();
                result.Add(orderedPositions.First().SecurityId, list);
                foreach (var pos in orderedPositions)
                {
                    list.Add(pos);
                    if (pos.CloseQuantity != 0)
                    {
                        break;
                    }
                }
            }
            return result;
        }

        public IEnumerable<int> GetMostUsedSecurities()
        {
            return RunUnitOfWork(dc =>
            {
                var context = ((IObjectContextAdapter)dc).ObjectContext;
                context.CommandTimeout = 0;

                return (from pos in dc.OriginalPositions
                        join fundBook in dc.FundBookEntities on pos.FundBookEntityId equals fundBook.Id
                        where fundBook.IsActive
                        group pos by pos.SecurityId
                            into gr
                            orderby gr.Count() descending
                            select gr.Key).ToList();
            });
        }
    }
}